/**
 ******************************************************************************
 * File Name          : freertos.c
 * Description        : Code for freertos applications
 ******************************************************************************
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "stdint.h"
#include <string.h>
#include "tx_api.h"
#include "framework.h"
#include "os_obj.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Variables -----------------------------------------------------------------*/

/* USER CODE BEGIN Variables */
// CHAR  _tx_version_id[100] =  "Copyright (c) Microsoft Corporation. All rights reserved.  * ThreadX 6.1";
/* USER CODE END Variables */

/* Function prototypes -------------------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

/* Hook prototypes */

/* USER CODE BEGIN Application */

/**
 * @brief  Start the RTOS Kernel with executing the specified thread.
 * @param  thread_def    thread definition referenced with \ref osThread.
 * @param  argument      pointer that is passed to the thread function as start argument.
 * @retval status code that indicates the execution status of the function
 * @note   MUST REMAIN UNCHANGED: \b osKernelStart shall be consistent in every CMSIS-RTOS.
 */
osStatus osKernelStart(void)
{
	// os entry
	tx_kernel_enter();

	return osOK;
}

/*********************** Thread Management *****************************/
/**
 * @brief  Create a thread and add it to Active Threads and set it to state READY.
 * @param  thread_def    thread definition referenced with \ref osThread.
 * @param  argument      pointer that is passed to the thread function as start argument.
 * @retval thread ID for reference by other functions or NULL in case of error.
 * @note   MUST REMAIN UNCHANGED: \b osThreadCreate shall be consistent in every CMSIS-RTOS.
 */
osThreadId osThreadCreate(const osThreadDef_t *thread_def, void *argument)
{
	void (*fun)(ULONG);
	UINT os_err;

	fun = (void (*)(ULONG))thread_def->pthread;

	os_err = tx_thread_create(thread_def->controlblock, // task tcb
							  thread_def->name,			// task name
							  fun,						// task entry
							  (ULONG)argument,			// task argv
							  thread_def->buffer,		// task stack
							  thread_def->stacksize,	// stack size
							  thread_def->tpriority,	// task priority
							  thread_def->tpriority,	// priority force sw
							  TX_NO_TIME_SLICE,			// time slice off
							  TX_AUTO_START);			// auto start

	os_api_exe_check();

	if (os_err)
	{
		dbg_print("creat thread fail : %s \n", thread_def->name);
	}
	else
	{
		dbg_print("creat thread ok : %32s , prio : %d\n",
				  thread_def->name,
				  thread_def->tpriority);
	}

	return thread_def->controlblock;
}

/*******************************************************************************
 * @brief: set priority
 * @param thread_id id
 * @param priority new priority
 * @return err
 ******************************************************************************/
UINT osThreadSetPriority(osThreadId thread_id, UINT priority)
{
	UINT old;
	return tx_thread_priority_change(thread_id, priority, &old);
}

/*********************** Generic Wait Functions *******************************/
/**
 * @brief   Wait for Timeout (Time Delay)
 * @param   millisec      time delay value
 * @retval  status code that indicates the execution status of the function.
 */
osStatus osDelay(uint32_t millisec)
{
	UINT os_err;

	os_err = tx_thread_sleep(millisec);

	(void)os_err;

	os_api_exe_check();

	return osOK;
}

/*******************   Message Queue Management Functions  *********************/

/**
 * @brief Create and Initialize a Message Queue
 * @param queue_def     queue definition referenced with \ref osMessageQ.
 * @param  thread_id     thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL.
 * @retval  message queue ID for reference by other functions or NULL in case of error.
 * @note   MUST REMAIN UNCHANGED: \b osMessageCreate shall be consistent in every CMSIS-RTOS.
 */
osMessageQId osMessageCreate(const osMessageQDef_t *queue_def, osThreadId thread_id)
{
	/* creat quene */
	tx_queue_create(queue_def->controlblock,  // Pointer to queue control block
					queue_def->name,		  // Pointer to queue name
					1,						  // Size of each queue message (Number of ULONG)
					queue_def->buffer,		  // Starting address of the queue area
					queue_def->queue_sz * 4); // Number of bytes in the queue

	return queue_def->controlblock;
}

/**
 * @brief Put a Message to a Queue.
 * @param  queue_id  message queue ID obtained with \ref osMessageCreate.
 * @param  info      message information.
 * @param  millisec  timeout value or 0 in case of no time-out.
 * @retval status code that indicates the execution status of the function.
 * @note   MUST REMAIN UNCHANGED: \b osMessagePut shall be consistent in every CMSIS-RTOS.
 */
osStatus osMessagePut(osMessageQId queue_id, uint32_t info, uint32_t millisec)
{
	ULONG sbuff[4];

	sbuff[0] = info;

	tx_queue_send(queue_id, sbuff, millisec);

	return osOK;
}

/**
 * @brief Get a Message or Wait for a Message from a Queue.
 * @param  queue_id  message queue ID obtained with \ref osMessageCreate.
 * @param  millisec  timeout value or 0 in case of no time-out.
 * @retval event information that includes status code.
 * @note   MUST REMAIN UNCHANGED: \b osMessageGet shall be consistent in every CMSIS-RTOS.
 */
osEvent osMessageGet(osMessageQId queue_id, uint32_t millisec)
{
	UINT os_err;
	ULONG rbuff[4];
	osEvent res;

	os_err = tx_queue_receive(queue_id, rbuff, millisec);

	if (os_err == TX_SUCCESS)
	{
		res.value.v = rbuff[0];
		res.status = osOK;
		res.def.message_id = queue_id;
	}
	else
	{
		res.value.v = 0;
		res.status = osErrorTimeoutResource;
		res.def.message_id = queue_id;
	}

	return res;
}

/// Create and Initialize a Semaphore object used for managing resources.
/// \param[in]     semaphore_def semaphore definition referenced with \ref osSemaphore.
/// \param[in]     count         number of available resources.
/// \return semaphore ID for reference by other functions or NULL in case of error.
/// \note MUST REMAIN UNCHANGED: \b osSemaphoreCreate shall be consistent in every CMSIS-RTOS.
osSemaphoreId osSemaphoreCreate(const osSemaphoreDef_t *semaphore_def, int32_t count)
{
	UINT os_err;

	os_err = tx_semaphore_create(
		semaphore_def->controlblock,
		semaphore_def->name,
		count);

	if (os_err == TX_SUCCESS)
	{
		return semaphore_def->controlblock;
	}

	return 0;
}

/// Wait until a Semaphore token becomes available.
/// \param[in]     semaphore_id  semaphore object referenced with \ref osSemaphoreCreate.
/// \param[in]     millisec      timeout value or 0 in case of no time-out.
/// \return number of available tokens, or -1 in case of incorrect parameters.
/// \note MUST REMAIN UNCHANGED: \b osSemaphoreWait shall be consistent in every CMSIS-RTOS.
int32_t osSemaphoreWait(osSemaphoreId semaphore_id, uint32_t millisec)
{
	UINT os_err;

	os_err = tx_semaphore_get(
		semaphore_id,
		millisec);

	if (os_err == TX_SUCCESS)
	{
		return semaphore_id->tx_semaphore_count;
	}

	return 0;
}

/// Release a Semaphore token.
/// \param[in]     semaphore_id  semaphore object referenced with \ref osSemaphoreCreate.
/// \return status code that indicates the execution status of the function.
/// \note MUST REMAIN UNCHANGED: \b osSemaphoreRelease shall be consistent in every CMSIS-RTOS.
osStatus osSemaphoreRelease(osSemaphoreId semaphore_id)
{
	UINT os_err;

	os_err = tx_semaphore_put(semaphore_id);

	if (os_err == TX_SUCCESS)
	{
		return osOK;
	}

	return osErrorParameter;
}

/// Delete a Semaphore that was created by \ref osSemaphoreCreate.
/// \param[in]     semaphore_id  semaphore object referenced with \ref osSemaphoreCreate.
/// \return status code that indicates the execution status of the function.
/// \note MUST REMAIN UNCHANGED: \b osSemaphoreDelete shall be consistent in every CMSIS-RTOS.
osStatus osSemaphoreDelete(osSemaphoreId semaphore_id)
{
	UINT os_err;

	os_err = tx_semaphore_delete(semaphore_id);

	if (os_err == TX_SUCCESS)
	{
		return osOK;
	}

	return osErrorParameter;
}

/*******************************************************************************
 * @brief: mutex creat
 * @param mutex_def
 * @return mutex id
 ******************************************************************************/
osMutexId osMutexCreate(const struct os_mutex_def *mutex_def)
{
	UINT os_err;
	os_err = tx_mutex_create(
		mutex_def->controlblock,
		mutex_def->name,
		TX_NO_INHERIT); // no prio

	if (os_err == TX_SUCCESS)
	{
		return mutex_def->controlblock;
	}

	return 0;
}

/// Create a timer.
/// \param[in]     timer_def     timer object referenced with \ref osTimer.
/// \param[in]     type          osTimerOnce for one-shot or osTimerPeriodic for periodic behavior.
/// \param[in]     argument      argument to the timer call back function.
/// \return timer ID for reference by other functions or NULL in case of error.
/// \note MUST REMAIN UNCHANGED: \b osTimerCreate shall be consistent in every CMSIS-RTOS.
osTimerId osTimerCreate(const osTimerDef_t *timer_def, os_timer_type type, void *argument)
{
	UINT os_err;

	os_err = tx_timer_create(
		timer_def->controlblock, timer_def->name,
		(void (*)(ULONG))timer_def->ptimer,
		(ULONG)argument,
		UINT32_MAX,								// first timeout value
		(type == osTimerOnce) ? 0 : UINT32_MAX, // 0 is one-shot, other timeout value for 2+ periodic
		TX_NO_ACTIVATE);						// auto active

	if (os_err == TX_SUCCESS)
	{
		return timer_def->controlblock;
	}

	return 0;
}

/// Start or restart a timer.
/// \param[in]     timer_id      timer ID obtained by \ref osTimerCreate.
/// \param[in]     millisec      time delay value of the timer.
/// \param[in]     type          osTimerOnce for one-shot or osTimerPeriodic for periodic behavior.
/// \return status code that indicates the execution status of the function.
/// \note MUST REMAIN UNCHANGED: \b osTimerStart shall be consistent in every CMSIS-RTOS.
osStatus osTimerStart(osTimerId timer_id, uint32_t millisec, os_timer_type type)
{
	UINT os_err;

	os_err = tx_timer_change(
		timer_id,
		ms_ticks(millisec),								 // first timeout value
		(type == osTimerOnce) ? 0 : ms_ticks(millisec)); // 0 is one-shot, other timeout value for 2+ periodic

	os_err = tx_timer_activate(timer_id);

	return os_err;
}

/* USER CODE END Application */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
